//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <elapi.h>
#include <vkey.h>
#include <aura.h>

_ELASTOS_NAMESPACE_USING

//
// CTRL, ALT, SHIFT status
//
#define KBD_CTRL                0x0001
#define KBD_ALT                 0x0002
#define KBD_SHIFT               0x0004
#define KBD_CAS                 0x0007
#define KBD_LCTRL               0x0100
#define KBD_LALT                0x0200
#define KBD_LSHIFT              0x0400
#define KBD_RCTRL               0x1000
#define KBD_RALT                0x2000
#define KBD_RSHIFT              0x4000

//
// Num Lock, Caps Lock, Scroll Lock
//
#define KBD_CAPLOCK             0x0004
#define KBD_NUMLOCK             0x0002

typedef struct KBDStatus {
    uint8_t     uScanCode;
    uint8_t     uVirKey;
    uint16_t    uFlags;          //Event type, one of up/down/special
    bool_t      bKeyPress;       // TRUE: Press; FALSE: Release
    uint16_t    uLedStatus;
    uint16_t    uKbdStatus;
    uint8_t     uKeyboardIdentifier;
}
KBDStatus;

static uint8_t uNumSymbols[] = {
    0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28
};

static uint8_t uGenericSymbols0[] = {
    0x3B, 0x3D, 0x2C, 0x2D, 0x2E, 0x2F
};

static uint8_t uGenericSymbols1[] = {
    0x3A, 0x2B, 0x3C, 0x5F, 0x3E, 0x3F
};

static uint8_t uGenericSymbols2[] = {
    0x5B, 0x5C, 0x5D, 0x27
};

static uint8_t uGenericSymbols3[] = {
    0x7B, 0x7C, 0x7D, 0x22
};

enum {
    ASCII_ESC = 0x1B,
    ASCII_LeftArrow = 0x1B,
    ASCII_RightArrow = 0x1A,
    ASCII_Insert = 0xE8,
    ASCII_Tab = 0x09,
    ASCII_BackSpace = 0x08,
    ASCII_Delete = 0x7F,
    ASCII_Home = 0x11,
    ASCII_End = 0x12,
    ASCII_UpArrow = 0x18,
    ASCII_DownArrow = 0x19,
    ASCII_PageUp = 0x1E,
    ASCII_PageDown = 0x1F,
    ASCII_SPACE = 0x20,
    ASCII_ENTER = 0x0D
};

//Translate keyboard data to Ascii
int _Impl_VcodeToAscii(uint8_t uVirtualcode,
    uint16_t uLedState, uint16_t uKeyboardState)
{
    if (VK_SPACE == uVirtualcode) {//SPACE
        return ASCII_SPACE;
    }

    //Letter key is pressed
    if (uVirtualcode >= VK_A && uVirtualcode <= VK_Z) {
        //Ctrl + (A-Z). Only Ctrl is pressed, while Shift and ALT are not pressed.
        if ((uKeyboardState & KBD_CAS) == KBD_CTRL) {
            return (uVirtualcode + 1 - VK_A);
        }

        //capital letter
        if (((uLedState & KBD_CAPLOCK) == KBD_CAPLOCK) ^
            ((uKeyboardState & KBD_CAS) == KBD_SHIFT)) {
            return uVirtualcode;
        }

        //small letter
        return (uVirtualcode + 0x20);
    }

    //VK_0 - VK_9 key is pressed
    if (uVirtualcode >= VK_0 && uVirtualcode <= VK_9) {
        //Only SHIFT and a number key are pressed
        if ((uKeyboardState & KBD_CAS) == KBD_SHIFT) { // symbols
            return uNumSymbols[uVirtualcode - VK_0];
        }
        //Ctrl, Shift and a number key are pressed while ALT is not pressed.(vi)
        else if ((uKeyboardState & KBD_CAS) == (KBD_SHIFT | KBD_CTRL)) {
            switch (uVirtualcode) {
                case VK_2://CTRL + @
                    return 0;
                case VK_6://CTRL + ^
                    return 0x1E;
                default:
                    return 0;
            }
        }
        else if (!uKeyboardState) { // Number
            return uVirtualcode;
        }
        else
            return 0;
    }

    //Ctrl + \,|, [,{, ],}, _,-.
    //Alt is not pressed, Ctrl is pressed.
    if ((uKeyboardState & KBD_CTRL) && (!(uKeyboardState & KBD_ALT))) {
        switch (uVirtualcode) {
            case VK_HYPHEN://Ctrl + -
                return 0x1F;
            case VK_LBRACKET://Ctrl + [
                return 0x1B;
            case VK_BACKSLASH://Ctrl + \, |
                return 0x1C;
            case VK_RBRACKET://Ctrl + ]
                return 0x1D;
            default:
                break;
        }
    }

    //Symbols:  ; = , - . /
    if (uVirtualcode >= VK_SEMICOLON && uVirtualcode <= VK_SLASH) {
        if (uKeyboardState & KBD_SHIFT) {
            return uGenericSymbols1[uVirtualcode - VK_SEMICOLON];
        }
        else {
            return uGenericSymbols0[uVirtualcode - VK_SEMICOLON];
        }
    }

    //Symbols:  ` ~
    if (VK_BACKQUOTE == uVirtualcode) {
        if (uKeyboardState & KBD_SHIFT) {
            return 0x7E;
        }
        else {
            return 0x60;
        }
    }

    //Symbols:  [ \ ] '
    if (uVirtualcode >= VK_LBRACKET && uVirtualcode <= VK_APOSTROPHE) {
        if (uKeyboardState & KBD_SHIFT) {
            return uGenericSymbols3[uVirtualcode - VK_LBRACKET];
        }
        else {
            return uGenericSymbols2[uVirtualcode - VK_LBRACKET];
        }
    }

    //Before this line, keyboard state affect the pressed key.
    //After this line, Only the a key pressed without ctrl, alt and shift
    if (uKeyboardState) {
        return 0;
    }

    //only a key in little keyboard is pressed
    if (uVirtualcode >= VK_NUMPAD0 && uVirtualcode <= VK_NUMPAD9) {
        return (uVirtualcode - 0x30);
    }

    //Little Keyboard Symbols
    if ((uVirtualcode >= VK_MULTIPLY) && (uVirtualcode <= VK_DIVIDE)) {
        if (uVirtualcode == VK_SEPARATOR)
            return ASCII_ENTER;
        else
            return (uVirtualcode - 0x40);
    }

    //F1~F10: return 0xBB~0xC4
    if ((uVirtualcode >= VK_F1) && (uVirtualcode <= VK_F10)) {
        return (uVirtualcode - VK_F1 + 0xBB);
    }

    //F11, F12: return 0xD7,0xD8
    if ((uVirtualcode >= VK_F11) && (uVirtualcode <= VK_F12)) {
        return (uVirtualcode - VK_F11 + 0xD7);
    }

    switch (uVirtualcode) {
        case VK_RETURN: // Enter
            return ASCII_ENTER;
        case VK_TAB:
            return ASCII_Tab;
        case VK_ESCAPE:
            return ASCII_ESC;
        case VK_LEFT:
            return ASCII_LeftArrow;
        case VK_RIGHT:
            return ASCII_RightArrow;
        case VK_INSERT:
            return ASCII_Insert;
        case VK_BACK:
            return ASCII_BackSpace;
        case VK_DELETE:
            return ASCII_Delete;
        case VK_HOME:
            return ASCII_Home;
        case VK_END:
            return ASCII_End;
        case VK_UP:
            return ASCII_UpArrow;
        case VK_DOWN:
            return ASCII_DownArrow;
        case VK_PRIOR:
            return ASCII_PageUp;
        case VK_NEXT:
            return ASCII_PageDown;
        default:
            return 0;
    }
}

IDeviceDriver *g_pConsole;
extern bool g_bUsingConsoleChannel;

ECode _Impl_GetChar(UInt32 toRead, MemoryBuf * pBuf, _ELASTOS Boolean bBuffer)
{
    if (g_bUsingConsoleChannel) {
        if (g_pConsole == NULL) { // get console device
            _CSystem_FindRunningObject(L"device:console0", (PInterface *)&g_pConsole);
        }
        if (g_pConsole != NULL) {
			//if (!bBuffer) { // not supported!
			//    ec = g_pConsole->Control(10, NULL_MEMORYBUF, NULL, NULL);
			//}
        	ECode ec = g_pConsole->Read(0, toRead, pBuf, NULL);
        	if (SUCCEEDED(ec)) {
        	   	return ec;
        	}
        }
    }


    if (!bBuffer) {
        *(char*)pBuf->GetPayload() = (char)Aura_getchar();
        pBuf->SetUsed(1);
    }
    else {
        UInt32 i = 0;
        int uchar = 0;
        for (i = 0; i < toRead; i++) {
            uchar = Aura_getchar();
            *((char*)pBuf->GetPayload() + i) = uchar;
            if (uchar == 0x0A) {
                break;
            }
        }
        pBuf->SetUsed(i + 1);
    }
    return NOERROR;
}
